package com.github.winyiwin;

import com.github.winyiwin.queue.QueueEnum;
import com.github.winyiwin.queue.RejectedHandlerEnum;
import com.github.winyiwin.threadfactory.KobeThreadFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.TimeUnit;

public class KobeThreadPoolManager {

    /**
     * 存放所有的线程池
     */
    private static final Map<String, KobeThreadPoolExecutor> EXECUTOR_MAP = new HashMap<>();
    /**
     * 存放所有线程池定义
     */
    private static final Map<String, ThreadPoolDefinition> DEFINITION_MAP = new HashMap<>();
    /**
     * 存放线程池拒绝数
     */
    private static final Map<String, Long> REJECT_MAP = new ConcurrentHashMap<>();

    public KobeThreadPoolExecutor getExecutor() {
        return getExecutor("");
    }

    public KobeThreadPoolExecutor getExecutor(String threadPoolName) {
        KobeThreadPoolExecutor executor = EXECUTOR_MAP.get(threadPoolName);
        if (executor == null) {
            throw new RuntimeException("找不到名为" + threadPoolName + "线程池");
        }
        return executor;
    }

    /**
     * 项目启动初始化加载
     * @param kobeThreadPoolProperties
     */
    public void init(KobeThreadPoolProperties kobeThreadPoolProperties) {
        kobeThreadPoolProperties.getThreadPools().forEach(definition -> {
            EXECUTOR_MAP.put(definition.getPoolName(), createExecutor(kobeThreadPoolProperties, definition));
            DEFINITION_MAP.put(definition.getPoolName(), definition);
        });
    }

    private KobeThreadPoolExecutor createExecutor(KobeThreadPoolProperties kobeThreadPoolProperties, ThreadPoolDefinition definition) {
        return new KobeThreadPoolExecutor(
                definition.getCoreSize(),
                definition.getMaxSize(),
                definition.getKeepAlive().getSeconds(),
                TimeUnit.SECONDS,
                getBlockingQueue(definition.getQueueType(), definition.getQueueCapacity(), false),
                new KobeThreadFactory(kobeThreadPoolProperties.getThreadNamePrefix() + definition.getPoolName() + "-"),
                getRejectedExecutionHandler(definition.getRejectedHandler(), definition.getPoolName()));
    }


    private RejectedExecutionHandler getRejectedExecutionHandler(RejectedHandlerEnum rejectHandler, String poolName) {
        if (RejectedHandlerEnum.getInstance(rejectHandler) != null) {
            return rejectHandler.createRejectedHandler();
        }
        return new KobeRejectedPolicy(poolName, REJECT_MAP);
    }

    private BlockingQueue getBlockingQueue(QueueEnum queueType, int queueCapacity, boolean fair) {
        if (QueueEnum.getInstance(queueType) != null) {
            return queueType.createQueue(queueCapacity, fair);
        }
        return new LinkedBlockingQueue(queueCapacity);
    }

    /*
    ################################################## public method
     */
    public KobeThreadPoolExecutor updateExecutor(ThreadPoolDefinition definition) {
        return EXECUTOR_MAP.computeIfPresent(definition.getPoolName(), (k, executor) -> {
            executor.setCorePoolSize(definition.getCoreSize());
            executor.setMaximumPoolSize(definition.getMaxSize());
            executor.setKeepAliveTime(definition.getKeepAlive().getSeconds(), TimeUnit.SECONDS);
            //executor.setRejectedExecutionHandler(getRejectedExecutionHandler(definition.g));
            return executor;
        });
    }

    public List<ThreadPoolStatusInfo> allThreadPoolStatusInfo() {
        List<ThreadPoolStatusInfo> poolStatusInfos = new ArrayList<>();
        for (String poolName : EXECUTOR_MAP.keySet()) {
            poolStatusInfos.add(threadPoolStatusInfo(poolName));
        }
        return poolStatusInfos;
    }

    public ThreadPoolStatusInfo threadPoolStatusInfo(String threadPoolName) {
        KobeThreadPoolExecutor executor = getExecutor(threadPoolName);
        ThreadPoolStatusInfo info = new ThreadPoolStatusInfo();
        info.setThreadPoolName(threadPoolName);
        info.setCorePoolSize(executor.getCorePoolSize());
        info.setMaximumPoolSize(executor.getMaximumPoolSize());
        info.setPoolSize(executor.getPoolSize());
        info.setLargestPoolSize(executor.getLargestPoolSize());
        info.setActiveCount(executor.getActiveCount());
        info.setCompletedTaskCount(executor.getCompletedTaskCount());
        info.setTaskCount(executor.getTaskCount());

        ThreadPoolDefinition poolDefinition = DEFINITION_MAP.get(threadPoolName);
        info.setQueueType(poolDefinition.getQueueType().name());
        BlockingQueue<Runnable> queue = executor.getQueue();
        info.setQueueRemainingCapacity(queue.remainingCapacity());
        info.setQueueSize(queue.size());

        info.setQueueCapacity(poolDefinition.getQueueCapacity());
        info.setPoolSize(executor.getPoolSize());
        info.setRejectCount(REJECT_MAP.getOrDefault(threadPoolName, 0L));
        info.setRejectedExecutionHandler(poolDefinition.getRejectedHandler().name());

        return info;
    }

}
